// 分布式锁装饰器

import 'reflect-metadata';
import { LockManager, LockOptions } from '../managers/LockManager';

// 全局锁管理器实例
let globalLockManager: LockManager | null = null;

/**
 * 设置全局锁管理器
 */
export function setLockManager(lockManager: LockManager): void {
  globalLockManager = lockManager;
}

/**
 * 获取全局锁管理器
 */
function getLockManager(): LockManager {
  if (!globalLockManager) {
    throw new Error('Lock manager not initialized. Call setLockManager() first.');
  }
  return globalLockManager;
}

export interface LockDecoratorOptions extends LockOptions {
  key?: string | ((args: any[]) => string);
  waitForLock?: boolean; // 是否等待锁释放
  onLockFailed?: (resource: string) => void | Promise<void>;
}

/**
 * 分布式锁装饰器
 */
export function Lock(options: LockDecoratorOptions = {}) {
  return function (
    target: any,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor
  ): PropertyDescriptor {
    const originalMethod = descriptor.value;

    if (typeof originalMethod !== 'function') {
      throw new Error('@Lock can only be applied to methods');
    }

    descriptor.value = async function (...args: any[]) {
      const lockManager = getLockManager();

      // 生成锁资源键
      const resource = generateLockKey(target, propertyKey, args, options.key);

      try {
        // 尝试获取锁
        const lockOptions: LockOptions = {
          retryCount: options.waitForLock ? (options.retryCount ?? 10) : 0,
        };
        if (options.ttl !== undefined) lockOptions.ttl = options.ttl;
        if (options.retryDelay !== undefined) lockOptions.retryDelay = options.retryDelay;

        const token = await lockManager.acquire(resource, lockOptions);

        if (!token) {
          // 获取锁失败
          if (options.onLockFailed) {
            await options.onLockFailed(resource);
          }

          throw new LockError(`Failed to acquire lock for resource: ${resource}`);
        }

        try {
          // 执行原方法
          return await originalMethod.apply(this, args);
        } finally {
          // 释放锁
          await lockManager.release(resource, token);
        }
      } catch (error) {
        if (error instanceof LockError) {
          throw error;
        }
        throw new Error(
          `Lock operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`
        );
      }
    };

    return descriptor;
  };
}

/**
 * 锁错误类
 */
export class LockError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'LockError';
  }
}

/**
 * 可重入锁装饰器
 */
export function ReentrantLock(
  options: LockDecoratorOptions & { owner?: string | ((args: any[]) => string) } = {}
): MethodDecorator {
  return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    if (typeof originalMethod !== 'function') {
      throw new Error('@ReentrantLock can only be applied to methods');
    }

    descriptor.value = async function (...args: any[]) {
      const lockManager = getLockManager();

      // 生成锁资源键和所有者
      const resource = generateLockKey(target, propertyKey, args, options.key);
      const owner = generateOwner(args, options.owner);

      try {
        // 尝试获取可重入锁
        const lockOptions: LockOptions = {};
        if (options.ttl !== undefined) lockOptions.ttl = options.ttl;
        if (options.retryDelay !== undefined) lockOptions.retryDelay = options.retryDelay;
        if (options.retryCount !== undefined) lockOptions.retryCount = options.retryCount;

        const acquired = await lockManager.acquireReentrant(resource, owner, lockOptions);

        if (!acquired) {
          if (options.onLockFailed) {
            await options.onLockFailed(resource);
          }

          throw new LockError(`Failed to acquire reentrant lock for resource: ${resource}`);
        }

        try {
          // 执行原方法
          return await originalMethod.apply(this, args);
        } finally {
          // 释放可重入锁
          await lockManager.releaseReentrant(resource, owner);
        }
      } catch (error) {
        if (error instanceof LockError) {
          throw error;
        }
        throw new Error(
          `Reentrant lock operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`
        );
      }
    };

    return descriptor;
  };
}

/**
 * 读锁装饰器
 */
export function ReadLock(options: LockDecoratorOptions = {}): MethodDecorator {
  return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    if (typeof originalMethod !== 'function') {
      throw new Error('@ReadLock can only be applied to methods');
    }

    descriptor.value = async function (...args: any[]) {
      const lockManager = getLockManager();

      // 生成锁资源键
      const resource = generateLockKey(target, propertyKey, args, options.key);

      try {
        // 尝试获取读锁
        const lockOptions: LockOptions = {};
        if (options.ttl !== undefined) lockOptions.ttl = options.ttl;
        if (options.retryDelay !== undefined) lockOptions.retryDelay = options.retryDelay;
        if (options.retryCount !== undefined) lockOptions.retryCount = options.retryCount;

        const readerId = await lockManager.acquireReadLock(resource, lockOptions);

        if (!readerId) {
          if (options.onLockFailed) {
            await options.onLockFailed(resource);
          }

          throw new LockError(`Failed to acquire read lock for resource: ${resource}`);
        }

        try {
          // 执行原方法
          return await originalMethod.apply(this, args);
        } finally {
          // 释放读锁
          await lockManager.releaseReadLock(resource, readerId);
        }
      } catch (error) {
        if (error instanceof LockError) {
          throw error;
        }
        throw new Error(
          `Read lock operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`
        );
      }
    };

    return descriptor;
  };
}

/**
 * 写锁装饰器
 */
export function WriteLock(options: LockDecoratorOptions = {}): MethodDecorator {
  return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    if (typeof originalMethod !== 'function') {
      throw new Error('@WriteLock can only be applied to methods');
    }

    descriptor.value = async function (...args: any[]) {
      const lockManager = getLockManager();

      // 生成锁资源键
      const resource = generateLockKey(target, propertyKey, args, options.key);

      try {
        // 尝试获取写锁
        const lockOptions: LockOptions = {};
        if (options.ttl !== undefined) lockOptions.ttl = options.ttl;
        if (options.retryDelay !== undefined) lockOptions.retryDelay = options.retryDelay;
        if (options.retryCount !== undefined) lockOptions.retryCount = options.retryCount;

        const writerId = await lockManager.acquireWriteLock(resource, lockOptions);

        if (!writerId) {
          if (options.onLockFailed) {
            await options.onLockFailed(resource);
          }

          throw new LockError(`Failed to acquire write lock for resource: ${resource}`);
        }

        try {
          // 执行原方法
          return await originalMethod.apply(this, args);
        } finally {
          // 释放写锁
          await lockManager.releaseWriteLock(resource, writerId);
        }
      } catch (error) {
        if (error instanceof LockError) {
          throw error;
        }
        throw new Error(
          `Write lock operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`
        );
      }
    };

    return descriptor;
  };
}

/**
 * 生成锁键
 */
function generateLockKey(
  target: any,
  propertyKey: string | symbol,
  args: any[],
  keyOption?: string | ((args: any[]) => string)
): string {
  if (typeof keyOption === 'function') {
    return keyOption(args);
  }

  if (typeof keyOption === 'string') {
    // 支持模板字符串
    return keyOption.replace(/\$\{args\[(\d+)\]\}/g, (match, index) => {
      const argIndex = parseInt(index, 10);
      return args[argIndex] !== undefined ? String(args[argIndex]) : match;
    });
  }

  // 默认键生成策略
  const className = target.constructor.name;
  const methodName = typeof propertyKey === 'symbol' ? propertyKey.toString() : propertyKey;

  if (args.length > 0 && args[0] != null) {
    return `${className}:${methodName}:${args[0]}`;
  }

  return `${className}:${methodName}`;
}

/**
 * 生成所有者标识
 */
function generateOwner(args: any[], ownerOption?: string | ((args: any[]) => string)): string {
  if (typeof ownerOption === 'function') {
    return ownerOption(args);
  }

  if (typeof ownerOption === 'string') {
    return ownerOption;
  }

  // 默认使用进程ID和时间戳
  return `${process.pid}-${Date.now()}`;
}

/**
 * 锁配置装饰器
 */
export function LockConfig(config: LockDecoratorOptions) {
  return function (_target: any) {
    Reflect.defineMetadata('lock:config', config, _target);
  };
}

/**
 * 获取类的锁配置
 */
export function getLockConfig(target: any): LockDecoratorOptions | undefined {
  return Reflect.getMetadata('lock:config', target);
}

/**
 * 锁键生成器
 */
export const LockKeyGenerators = {
  /**
   * 基于用户ID
   */
  userId:
    (userIdIndex: number = 0) =>
    (args: any[]) =>
      `user:${args[userIdIndex]}`,

  /**
   * 基于资源ID
   */
  resourceId:
    (resourceIdIndex: number = 0) =>
    (args: any[]) =>
      `resource:${args[resourceIdIndex]}`,

  /**
   * 基于对象属性
   */
  objectProperty:
    (property: string, objectIndex: number = 0) =>
    (args: any[]) => {
      const obj = args[objectIndex];
      return obj?.[property] ? String(obj[property]) : 'unknown';
    },

  /**
   * 组合键
   */
  composite:
    (...generators: Array<(args: any[]) => string>) =>
    (args: any[]) =>
      generators.map(gen => gen(args)).join(':'),

  /**
   * 基于方法参数
   */
  methodArgs:
    (separator: string = ':') =>
    (args: any[]) =>
      args.join(separator),

  /**
   * 基于类名和方法名
   */
  classMethod: (className: string, methodName: string) => () => `${className}:${methodName}`,
};

/**
 * 所有者生成器
 */
export const OwnerGenerators = {
  /**
   * 基于用户ID
   */
  userId:
    (userIdIndex: number = 0) =>
    (args: any[]) =>
      `user:${args[userIdIndex]}`,

  /**
   * 基于会话ID
   */
  sessionId:
    (sessionIdIndex: number = 0) =>
    (args: any[]) =>
      `session:${args[sessionIdIndex]}`,

  /**
   * 基于线程ID
   */
  threadId: () => () => `thread:${process.pid}:${Date.now()}`,

  /**
   * 基于请求ID
   */
  requestId:
    (requestIdIndex: number = 0) =>
    (args: any[]) =>
      `request:${args[requestIdIndex]}`,

  /**
   * 静态所有者
   */
  static: (owner: string) => () => owner,
};

/**
 * 锁超时装饰器
 */
export function LockWithTimeout(timeout: number, options: Omit<LockDecoratorOptions, 'ttl'> = {}) {
  return Lock({
    ...options,
    ttl: timeout,
  });
}

/**
 * 快速失败锁装饰器（不等待）
 */
export function TryLock(options: Omit<LockDecoratorOptions, 'waitForLock' | 'retryCount'> = {}) {
  return Lock({
    ...options,
    waitForLock: false,
    retryCount: 0,
  });
}

/**
 * 等待锁装饰器
 */
export function WaitForLock(
  maxWaitTime: number = 30,
  options: Omit<LockDecoratorOptions, 'waitForLock' | 'retryCount'> = {}
) {
  return Lock({
    ...options,
    waitForLock: true,
    retryCount: Math.floor((maxWaitTime * 1000) / (options.retryDelay ?? 100)),
  });
}
